package com.huawei.admins.platform.gateway.core.config;

import com.huawei.admins.platform.gateway.core.component.filter.factory.RequestRateLimiterGatewayFilterFactory;
import com.huawei.admins.platform.gateway.core.component.filter.factory.RetryGatewayFilterFactory;
import com.huawei.admins.platform.gateway.core.component.filter.factory.RewritePathGatewayFilterFactory;
import com.huawei.admins.platform.gateway.core.component.limiter.KeyResolver;
import com.huawei.admins.platform.gateway.core.component.limiter.LimiterLuaScript;
import com.huawei.admins.platform.gateway.core.component.limiter.RateLimiter;
import com.huawei.admins.platform.gateway.core.component.limiter.RedisRateLimiter;
import com.huawei.admins.platform.gateway.core.component.limiter.ServiceRateLimiter;
import com.huawei.admins.platform.gateway.core.component.limiter.UrlPathKeyResolver;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.script.DigestUtils;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.reactive.DispatcherHandler;

import java.util.List;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({DispatcherHandler.class, HttpHandler.class})
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class GatewayFilterConfiguration {

    private GatewayProperties properties;

    public GatewayFilterConfiguration(GatewayProperties properties) {
        this.properties = properties;
    }

    @Bean
    public RewritePathGatewayFilterFactory rewritePathGatewayFilterFactory() {
        return new RewritePathGatewayFilterFactory(properties);
    }

    @Bean
    public RequestRateLimiterGatewayFilterFactory rateLimiterGatewayFilterFactory(RateLimiter rateLimiter, KeyResolver resolver) {
        return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver, properties);
    }

    @Bean
    public RetryGatewayFilterFactory retryGatewayFilterFactory() {
        return new RetryGatewayFilterFactory(properties);
    }

    @Bean(name = {"urlPathKeyResolver"})
    public KeyResolver urlPathKeyResolver() {
        return new UrlPathKeyResolver();
    }

    @Bean(name = {"rateLimiterLuaScript"})
    @SuppressWarnings({"unchecked", "rawtypes"})
    public LimiterLuaScript limiterLuaScript() {
        LimiterLuaScript limiterLuaScript = new LimiterLuaScript();
        limiterLuaScript.setScriptSource(
                new ResourceScriptSource(new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
        limiterLuaScript.setResultType(List.class);
        limiterLuaScript.setSha1(DigestUtils.sha1DigestAsHex(limiterLuaScript.getScriptAsString()));
        return limiterLuaScript;
    }

    @Bean(name = {"gatewayRateLimiter"})
    public RateLimiter serviceRateLimiterManager(ReactiveStringRedisTemplate redisTemplate,
                                                 @Qualifier("rateLimiterLuaScript") RedisScript<List<Long>> redisScript) {
        return new ServiceRateLimiter(new RedisRateLimiter(redisTemplate, redisScript));
    }

    public GatewayProperties getProperties() {
        return properties;
    }

    public void setProperties(GatewayProperties properties) {
        this.properties = properties;
    }
}
